import pytest
import requests
from unittest.mock import patch

# 定义测试数据
TEST_ZONES = [
    {
        "Zone": "ap-beijing-1",
        "ZoneName": "北京一区",
        "ZoneState": "AVAILABLE",
        "AvailableResourceCreation": ["CVM"]
    },
    {
        "Zone": "ap-guangzhou-2",
        "ZoneName": "广州二区",
        "ZoneState": "AVAILABLE",
        "AvailableResourceCreation": ["CVM", "LB"]
    }
]


# 定义全局fixture
@pytest.fixture(scope="module")
def api_url():
    return "https://cvm.tencentcloudapi.com"


@pytest.fixture
def headers():
    return {
        "Content-Type": "application/json",
        "X-TC-Action": "DescribeZones"
    }


# 测试类
class TestDescribeZones:

    # 正常用例：验证正常响应结构
    def test_normal_response(self, mocker, headers, api_url):
        mock_response = {
            "Response": {
                "TotalCount": 2,
                "ZoneSet": TEST_ZONES,
                "RequestId": "mock-request-id"
            }
        }

        with patch.object(requests, 'post') as mock_post:
            mock_post.return_value.status_code = 200
            mock_post.return_value.json.return_value = mock_response

            response = requests.post(api_url, headers=headers)

            assert response.status_code == 200
            assert response.json()["Response"]["TotalCount"] == 2
            assert len(response.json()["Response"]["ZoneSet"]) == 2

            for zone in response.json()["Response"]["ZoneSet"]:
                assert "Zone" in zone
                assert "ZoneName" in zone
                assert "ZoneState" in zone
                assert isinstance(zone["AvailableResourceCreation"], list)

    # 异常场景测试
    def test_network_error(self, mocker):
        with patch.object(requests, 'post', side_effect=requests.exceptions.ConnectionError):
            with pytest.raises(requests.exceptions.ConnectionError):
                requests.post(api_url, headers=headers)

    def test_invalid_json_response(self, mocker, api_url, headers):
        invalid_response = {"InvalidKey": "test"}

        with patch.object(requests, 'post') as mock_post:
            mock_post.return_value.status_code = 200
            mock_post.return_value.json.side_effect = ValueError("Invalid JSON")

            with pytest.raises(ValueError):
                requests.post(api_url, headers=headers).json()

    def test_missing_required_fields(self, mocker, api_url, headers):
        # 模拟缺少必需字段（如TotalCount）
        invalid_response = {"Response": {}}  # 缺少TotalCount等必需字段

        with patch.object(requests, 'post') as mock_post:
            mock_post.return_value.status_code = 200
            mock_post.return_value.json.return_value = invalid_response

            with pytest.raises(KeyError):
                response = requests.post(api_url, headers=headers)
                _ = response.json()["Response"]["TotalCount"]  # 尝试访问不存在的字段

    def test_error_response_handling(self, mocker, api_url, headers):
        error_response = {
            "Response": {
                "Error": {
                    "Code": "InvalidParameter",
                    "Message": "Invalid parameter value"
                }
            }
        }

        with patch.object(requests, 'post') as mock_post:
            mock_post.return_value.status_code = 400
            mock_post.return_value.json.return_value = error_response

            response = requests.post(api_url, headers=headers)
            assert "Error" in response.json().get("Response", {}), "Expected error not found"


# 参数化测试
@pytest.mark.parametrize("offset, limit, expected_count", [
    (0, 1, 1),
    (1, 1, 1),
    (0, 100, len(TEST_ZONES))
])
def test_pagination(api_url, headers, offset, limit, expected_count):
    mock_response = {
        "Response": {
            "TotalCount": len(TEST_ZONES),
            "ZoneSet": TEST_ZONES[offset:offset + limit],
            "RequestId": "mock-request-id"
        }
    }

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = mock_response

        response = requests.post(
            api_url,
            headers=headers,
            json={
                "Offset": offset,
                "Limit": limit
            }
        )

        assert len(response.json()["Response"]["ZoneSet"]) == expected_count


@pytest.mark.parametrize("filter_name, filter_values", [
    ("Zone", ["ap-beijing-1"]),
    ("ZoneState", ["AVAILABLE"]),
    ("AvailableResourceCreation", ["CVM"])
])
def test_filtering(api_url, headers, filter_name, filter_values):
    filtered_zones = [z for z in TEST_ZONES if z[filter_name] in filter_values]

    params = {
        "Filters": [{
            "Name": filter_name,
            "Values": filter_values
        }]
    }

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = {
            "Response": {
                "ZoneSet": filtered_zones,
                "TotalCount": len(filtered_zones),
                "RequestId": "mock-request-id"
            }
        }

        response = requests.post(api_url, headers=headers, json=params)

        assert all(z[filter_name] in filter_values for z in response.json()["Response"]["ZoneSet"])


def test_zero_limit(api_url, headers):
    mock_response = {
        "Response": {
            "TotalCount": 0,
            "ZoneSet": [],
            "RequestId": "mock-request-id"
        }
    }

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 200
        mock_post.return_value.json.return_value = mock_response

        response = requests.post(api_url, headers=headers, json={"Limit": 0})

        assert response.json()["Response"]["TotalCount"] == 0
        assert len(response.json()["Response"]["ZoneSet"]) == 0


def test_invalid_filter_value(api_url, headers):
    error_response = {
        "Response": {
            "Error": {
                "Code": "InvalidParameterValue",
                "Message": "Filter value not supported"
            }
        }
    }

    with patch.object(requests, 'post') as mock_post:
        mock_post.return_value.status_code = 400
        mock_post.return_value.json.return_value = error_response

        response = requests.post(api_url, headers=headers, json={
            "Filters": [{"Name": "InvalidFilter", "Values": ["InvalidValue"]}]
        })

        # 验证状态码和错误信息，而非抛出异常
        assert response.status_code == 400
        assert "Error" in response.json().get("Response", {})
        assert error_response["Response"]["Error"]["Code"] == "InvalidParameterValue"